package xx.notice.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import xx.notice.dao.NoteAcceptDao;
import xx.notice.dao.NoteSourceDao;
import xx.notice.dao.UserDao;
import xx.notice.entity.NoteAccept;
import xx.notice.entity.NoteSource;
import org.springframework.stereotype.Service;
import xx.notice.entity.Result;
import xx.notice.service.NoteSourceService;
import xx.notice.websocket.NoticeWebsocket;

import javax.annotation.Resource;
import java.util.*;

import static xx.notice.common.Constants.*;

/**
 * 与“通知”本身相关的具体信息，包括通知的内容、通知的类型和通知的对象类型等(NoteSource)表服务实现类
 *
 * @author halo-king
 * @since 2023-06-13 14:17:19
 */
@Service("noteSourceService")
public class NoteSourceServiceImpl extends ServiceImpl<NoteSourceDao, NoteSource> implements NoteSourceService {

    @Resource
    UserDao userDao;

    @Resource
    NoteAcceptDao noteAcceptDao;

    /**
     * 发布新通知
     *
     * @param notice
     */
    @Override
    public Result publishNotice(NoteSource notice) {
        handelNoticeType(notice);
        return null;
    }

    /**
     * 首先判断通知类型  0：实时通知；1：登录时通知；2：定时通知
     * 其次获取到通知人 个人还是所有人
     *
     * @param noteSource
     */
    private void handelNoticeType(NoteSource noteSource) {
        // 实时通知
        if (REAL_NOTICE_TYPE.equals(noteSource.getNoteType())) {
            noteSource.setRegularTime(null);
            // 处理接受人类型
            HandleDto handleDto = handelNoticeAcceptType(noteSource);
            NoteAccept accept = handleDto.acceptDto;
            handleDto.users.forEach(user->{
                accept.setStaffId(user);
                // 数据持久化到数据库
                this.noteAcceptDao.insert(accept);
                NoticeWebsocket.sendNotice(noteSource,user);
            });
        }
        // 定时通知
        else if (REGULAR_NOTICE_TYPE.equals(noteSource.getNoteType())) {
            final Date regularTime = noteSource.getRegularTime();
            long currentTime = System.currentTimeMillis();
            if (regularTime == null || regularTime.getTime() <= currentTime) {
                // 定时发布时间为空或小于当前时间，参数无效
                System.out.println("时间参数不合法");
            }
            noteSource.setRegularTime(new Date());
            // 处理接受人类型
            HandleDto handleDto = handelNoticeAcceptType(noteSource);
            NoteAccept accept = handleDto.acceptDto;
            handleDto.users.forEach(user->{
                accept.setStaffId(user);
                // 数据持久化到数据库
                this.noteAcceptDao.insert(accept);
            });
        }
        // 登录时通知
        else if (LOGIN_NOTICE_TYPE.equals(noteSource.getNoteType())) {
            noteSource.setRegularTime(null);
            // 处理接受人类型
            HandleDto handleDto = handelNoticeAcceptType(noteSource);
            NoteAccept accept = handleDto.acceptDto;
            handleDto.users.forEach(user->{
                accept.setStaffId(user);
                // 数据持久化到数据库
                this.noteAcceptDao.insert(accept);
            });
        } else {
            System.out.println("没有匹配的通知类型");
        }
    }

    /**
     * 处理接受人类型
     * @param noteSource
     * @return
     */
    private HandleDto handelNoticeAcceptType(NoteSource noteSource) {
        NoteAccept noteAccept = new NoteAccept();
        List<String> users = new ArrayList<>();
        // 接受人为所有人
        if (ALL_NOTICE_RECEIVE_TYPE.equals(noteSource.getAcceptType())) {
            noteSource.setAcceptObj("all");
            users = userDao.queryUserList();
        }
        // 接受人为个人
        else if (ONLY_ONE_NOTICE_RECEIVE_TYPE.equals(noteSource.getAcceptType())) {
            noteSource.setAcceptObj(noteSource.getAcceptObj());
            users.add(noteSource.getAcceptObj());
        }
        // 保存通知详情至数据库
        noteSource.setCreateTime(new Date());
        this.baseMapper.insert(noteSource);
        // 设置接受通知主键
        noteAccept.setNoteId(noteSource.getNoteId());
        // 设置通知类型
        noteAccept.setNoteType(noteSource.getNoteType());
        // 设置创建时间
        noteAccept.setCreateTime(new Date());
        return new HandleDto(users, noteAccept);
    }

    /**
     * 根据通知类型，进行不同处理
     * - 0：即时通知；1：登录时通知；2：定时通知
     *
     * @param notice
     */
    private int handleNoticeByType(NoteSource notice) {
        // 默认参数有误
        int code = 0;
        // 获取通知类型
        Integer noteType = notice.getNoteType();
        // 一切正常，封装通知接收的信息，消息接收对象
        NoteAccept acceptDto;
        // 判断通知的类型 即时通知
        if ("0".equals(noteType)) {
            // 如果是“即时通知”,继续按“接收人类型”处理
            notice.setRegularTime(null);
            // 接受人类型处理 所有人、指定人
            HandleDto handleDto = handleNoticeByAcceptType(notice);
            // 如果一切处理正常，封装通知接收信息
            acceptDto = handleDto.acceptDto;
            acceptDto.setNoteType(noteType);
            // 遍历通知的接受对象，并将通知的接受信息保存到数据库
            for (String user : handleDto.users) {
                // 将通知接收信息保存到数据库
                acceptDto.setStaffId(user);
                // 标记为“已通知”
                acceptDto.setNoteRes(1);
                // 保存接受人及相关通知消息
                noteAcceptDao.insert(acceptDto);
                // 赋值通知的主键
                notice.setNoteId(acceptDto.getNoteId());
                // 立即通知在线用户
                NoticeWebsocket.sendNotice(notice, user);
            }
            // 标记通知发布成功
            code = 1;
        }
        // 如果是“登录时通知”,继续按“接收人类型”处理
        else if ("2".equals(noteType)) {
            notice.setRegularTime(null);
            HandleDto handleDto = handleNoticeByAcceptType(notice);
            // 封装通知接收信息
            handleNoticeAcceptInfo(handleDto, noteType);
            // 标记通知发布成功
            code = 1;
        }
        // 如果是“定时通知”，需要先检验“定时发布的时间”参数
        else if ("3".equals(noteType)) {
            final Date regularTime = notice.getRegularTime();
            long currentTime = System.currentTimeMillis();
            if (regularTime == null || regularTime.getTime() <= currentTime) {
                // 定时发布时间为空或小于当前时间，参数无效
                return -1;
            }
            // 继续按“接收人类型”处理
            HandleDto handleDto = handleNoticeByAcceptType(notice);
            if (handleDto.code < 1) {
                return handleDto.code;
            }
            // 封装通知接收信息
            handleNoticeAcceptInfo(handleDto, noteType);
            // 创建定时任务：发送定时通知
            for (String user : handleDto.users) {
                //NoticeWebsocket.scheduleRegularNote(notice, user);
            }
            // 标记通知发布成功
            code = 1;
        }
        return code;
    }

    /**
     * 保存通知接收信息到数据库中
     *
     * @param handleDto
     * @param noteType
     */
    private void handleNoticeAcceptInfo(HandleDto handleDto, int noteType) {
        // 如果一切处理正常，封装通知接收信息
        NoteAccept acceptDto = handleDto.acceptDto;
        acceptDto.setNoteType(noteType);
        // 标记“未通知”
        acceptDto.setNoteRes(0);
        // 遍历通知的接受对象，并将通知的接受信息保存到数据库
        for (String user : handleDto.users) {
            // 将通知接收信息保存到数据库
            acceptDto.setStaffId(user);
            noteAcceptDao.insert(acceptDto);
        }
    }

    /**
     * 按通知接收人的类型，对通知进行不同处理
     */
    private HandleDto handleNoticeByAcceptType(NoteSource notice) {
        // 返回的结果
        HandleDto handleDto = new HandleDto();
        // 获取接收人类型
        Integer acceptType = notice.getAcceptType();
        // 通知的受众
        List<String> users = null;
        String acceptObj = notice.getAcceptObj();
        // 所有人
        if ("1".equals(acceptType)) {
            notice.setAcceptObj("all");
            // 如果通知“全部员工”，获取全部员工
            users = userDao.queryUserList();
        }
        // 指定个人
        else if ("2".equals(acceptType)) {
            users = new ArrayList<String>(1);
            users.add(acceptObj);
        } else {
            return handleDto;
        }

        // 检验接收对象
        if (users == null || users.size() == 0) {
            return handleDto;
        }

        // 保存“通知”到数据库
        Integer noteId = this.baseMapper.insert(notice);
        // 一切正常，封装通知接收的信息
        NoteAccept acceptDto = new NoteAccept();
        acceptDto.setNoteId(noteId);
        // 返回通知的员工
        return new HandleDto(1, users, acceptDto);
    }

    /**
     * 用于数据传输的内部类
     */
    private class HandleDto {
        int code;
        List<String> users;
        NoteAccept acceptDto;

        HandleDto() {
            this.code = 0;
        }

        HandleDto(int code) {
            this.code = code;
        }

        HandleDto(List<String> users, NoteAccept acceptDto) {
            this.users = users;
            this.acceptDto = acceptDto;
        }

        HandleDto(int code, List<String> users, NoteAccept acceptDto) {
            this.code = code;
            this.users = users;
            this.acceptDto = acceptDto;
        }
    }
}
